// 用 setTimeout实现的promise
// promise的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

const resolvePromise = (promise2, res, resolve, reject) => {
  if(promise2 === res) {
    return reject(new TypeError('不可以循环调用自身'))
  }
  
  if(res instanceof MyPromise) {
    // 如果是promise，继续将回调链式传递下去
    res.then(resolve, reject);
  } else {
    // 如果是普通值，则直接将状态改变返回
    resolve(res);
  }
}

class MyPromise {

  status = PENDING;
  value = undefined;
  error = undefined;
  successCallback = [];
  errorCallback = [];

  constructor(executor) {
    // 构造函数为一个方法，该方法会立即执行。
    // 该方法带有两个回调参数，即promise对象的resolve方法和reject方法，
    // 调用其中一个方法即会改变promise对象状态
    try {
      // 如果初始化过程中出现异常，则直接调用reject
      executor(this.resolve, this.reject)
    } catch (err) {
      this.reject(err);
    }
  }

  resolve = value => {
    // 如果promise对象状态已经修改，则立即返回
    if (this.status !== PENDING) return;

    this.status = FULFILLED;// 更新对象状态
    this.value = value; // 保存值，作为回调的入参
    // 执行successCallback中所有回调
    while (this.successCallback.length) {
      this.successCallback.shift()();
    }
  }

  reject = error => {
    // 如果promise对象状态已经修改，则立即返回
    if (this.status !== PENDING) return;

    this.status = REJECTED;
    this.error = error; // 保存值，作为回调的入参
    // 执行errorCallback中所有回调
    while (this.errorCallback.length) {
      this.errorCallback.shift()();
    }
  }

  then = (successCallback, errorCallback) => {
    // 给参数设置默认值，且参数必须为函数
    successCallback = successCallback && typeof successCallback === 'function' ? successCallback : value => value
    errorCallback = errorCallback && typeof errorCallback === 'function' ? errorCallback : error => { throw error; }

    // then也需返回一个promise，所以在这里创建一个新的promise对象，并在实参方法中执行相关操作
    let promise2 = new MyPromise((resolve, reject) => {
      // 判断当前对象的状态
      if (this.status === FULFILLED) {
        // 成功
        setTimeout(() => {
          try {
            let res = successCallback(this.value);
            // 回调返回值res可能是普通值，也可能还是一个promise对象
            // 根据res值的类型判断是调用resolve还是reject方法，同时要避免循环调用
            resolvePromise(promise2, res, resolve, reject);
          } catch(error) {
            reject(error);
          }
        }, 0);
      } else if(this.status === REJECTED) {
        // 失败
        setTimeout(() => {
          try {
            let res = errorCallback(this.error);
            // 回调返回值res可能是普通值，也可能还是一个promise对象
            // 根据res值的类型判断是调用resolve还是reject方法，同时要避免循环调用
            resolvePromise(promise2, res, resolve, reject);
          } catch(error) {
            reject(error);
          }
        }, 0);
      } else {
        // 等待对象状态改变，则先讲回调保存起来，调用resolve或reject方法的时候再执行
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let res = successCallback(this.value);
              // 回调返回值res可能是普通值，也可能还是一个promise对象
              // 根据res值的类型判断是调用resolve还是reject方法，同时要避免循环调用
              resolvePromise(promise2, res, resolve, reject);
            } catch(error) {
              reject(error);
            }
          }, 0);
        });
        this.errorCallback.push(() => {
          setTimeout(() => {
            try {
              let res = errorCallback(this.error);
              // 回调返回值res可能是普通值，也可能还是一个promise对象
              // 根据res值的类型判断是调用resolve还是reject方法，同时要避免循环调用
              resolvePromise(promise2, res, resolve, reject);
            } catch(error) {
              reject(error);
            }
          }, 0);
        })
      }
    })

    return promise2;
  }

  finally = callback => {
    //return this.then(callback, error => { throw error; });
    // 如果采用上面的写法，则finally后面再跟then时，入参将会丢失
    // 所以可以通过Promise.resolve将callback转为promise，并将进入finally之前操作的返回值继续传递给后面的then操作
    // finally操作是没有自己的返回值的
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value);
    }, error => {
      return MyPromise.resolve(callback()).then(() => { throw error; });
    })
  }

  catch = errorCallback => {
    return this.then(undefined, errorCallback);
  }

  static all = array => {
    let result = [];
    let index = 0; // 用于判断入参所有状态是否都已修改

    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        index++;
        result[key] = value;
        if(index === array.length) {
          resolve(result);
        }
      }

      for(let i = 0; i < array.length; i++) {
        let current = array[i];
        if(current instanceof MyPromise) {
          // 如果是promise对象，则在then回调中执行addData方法
          current.then(value => addData(i, value), error => reject(error))
        } else {
          // 如果是普通值，则直接执行addData方法
          addData(i, current);
        }
      }
    })
  }

  static race = array => {
    let isReturn = false;
    return new MyPromise((resolve, reject) => {
      function returnFunc(value) {
        if(!isReturn) {
          // 状态有更新就立马更改promise状态，且后面的回调不再有效果
          isReturn = true;
          resolve(value);
        }
      }

      for(let i = 0; i < array.length; i++) {
        let current = array[i];
        if(current instanceof MyPromise) {
          // 如果是promise对象，则不管状态是改为成功还是失败，race方法都马上返回(改变状态)
          current.then(returnFunc, returnFunc);
        } else {
          returnFunc(current);
        }
      }
    })
  }

  static resolve = value => {
    if(value instanceof MyPromise) {
      return value;
    } else {
      return new MyPromise((resolve, reject) => resolve(value));
    }
  }
}

module.exports = MyPromise;